import QtQuick 2.7
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import MaterialIcons 2.2
import QtQml.Models 2.2

import Utils 1.0

/**
 * ImageGallery displays as a grid of Images a model containing Viewpoints objects.
 * It manages a model of multiple CameraInit nodes as individual groups.
 */
Panel {
    id: root

    property variant cameraInits
    property variant cameraInit
    readonly property alias currentItem: grid.currentItem
    readonly property string currentItemSource: grid.currentItem ? grid.currentItem.source : ""
    readonly property var currentItemMetadata: grid.currentItem ? grid.currentItem.metadata : undefined
    property int defaultCellSize: 160
    property int currentIndex: 0
    property bool readOnly: false
    readonly property variant viewpoints: cameraInit.attribute('viewpoints').value

    signal removeImageRequest(var attribute)
    signal filesDropped(var drop, var augmentSfm)

    title: "Images"
    implicitWidth: (root.defaultCellSize + 2) * 2

    ColumnLayout {
        anchors.fill: parent
        spacing: 4

        GridView {
            id: grid

            Layout.fillWidth: true
            Layout.fillHeight: true

            ScrollBar.vertical: ScrollBar {}

            focus: true
            clip: true
            cellWidth: thumbnailSizeSlider.value
            cellHeight: cellWidth
            highlightFollowsCurrentItem: true
            keyNavigationEnabled: true

            // Update grid current item when selected view changes
            Connections {
                target: _reconstruction
                onSelectedViewIdChanged: {
                    var idx = grid.model.find(_reconstruction.selectedViewId, "viewId")
                    if(idx >= 0)
                        grid.currentIndex = idx
                }
            }

            model: SortFilterDelegateModel {
                id: sortedModel
                model: _reconstruction.viewpoints
                sortRole: "path"
                // TODO: provide filtering on reconstruction status
                // filterRole: _reconstruction.sfmReport ? "reconstructed" : ""
                // filterValue: true / false
                // in modelData:
                // if(filterRole == roleName)
                //     return _reconstruction.isReconstructed(item.model.object)

                // override modelData to return basename of viewpoint's path for sorting
                function modelData(item, roleName) {
                    var value = item.model.object.value.get(roleName).value
                    if(roleName == sortRole)
                        return Filepath.basename(value)
                    else
                        return value
                }

                delegate: ImageDelegate {

                    viewpoint: object.value
                    width: grid.cellWidth
                    height: grid.cellHeight
                    readOnly: root.readOnly

                    isCurrentItem: GridView.isCurrentItem

                    onIsCurrentItemChanged: {
                        if(isCurrentItem)
                            _reconstruction.selectedViewId = viewpoint.get("viewId").value
                    }

                    onPressed: {
                        grid.currentIndex = DelegateModel.filteredIndex
                        if(mouse.button == Qt.LeftButton)
                            grid.forceActiveFocus()
                    }

                    function sendRemoveRequest()
                    {
                        if(!readOnly)
                            removeImageRequest(object)
                    }

                    onRemoveRequest: sendRemoveRequest()
                    Keys.onDeletePressed: sendRemoveRequest()

                    Row {
                        anchors.top: parent.top
                        anchors.right: parent.right
                        anchors.margins: 4
                        spacing: 2

                        property bool valid: Qt.isQtObject(object) // object can be evaluated to null at some point during creation/deletion
                        property bool noMetadata: valid && !_reconstruction.hasMetadata(model.object)
                        property bool noIntrinsic: valid  && !_reconstruction.hasValidIntrinsic(model.object)
                        property bool inViews: valid && _reconstruction.sfmReport && _reconstruction.isInViews(object)

                        // Missing metadata indicator
                        Loader {
                            active: parent.noMetadata
                            visible: active
                            sourceComponent: MaterialLabel {
                                text: MaterialIcons.info_outline
                                color: "#FF9800"
                                ToolTip.text: "No Metadata"
                            }
                        }
                        // Unknown camera instrinsics indicator
                        Loader {
                            active: parent.noIntrinsic
                            visible: active
                            sourceComponent: MaterialLabel {
                                text: MaterialIcons.camera
                                color: "#FF9800"
                                ToolTip.text: "No Camera Instrinsic Parameters (missing Metadata?)"
                            }
                        }
                        // Reconstruction status indicator
                        Loader {
                            active: parent.inViews
                            visible: active
                            sourceComponent: MaterialLabel {
                                property bool reconstructed: _reconstruction.isReconstructed(model.object)
                                text: reconstructed ? MaterialIcons.check_circle : MaterialIcons.remove_circle
                                color: reconstructed ? "#4CAF50" : "#F44336"
                                ToolTip.text: reconstructed ? "Reconstructed" : "Not Reconstructed"
                            }
                        }
                    }
                }
            }

            // Keyboard shortcut to change current image group
            Keys.priority: Keys.BeforeItem
            Keys.onPressed: {
                if(event.modifiers & Qt.AltModifier)
                {
                    event.accepted = true
                    if(event.key == Qt.Key_Right)
                        root.currentIndex = Math.min(root.cameraInits.count - 1, root.currentIndex + 1)
                    else if(event.key == Qt.Key_Left)
                        root.currentIndex = Math.max(0, root.currentIndex - 1)
                }
            }

            // Explanatory placeholder when no image has been added yet
            Column {
                anchors.centerIn: parent
                visible: grid.model.count == 0
                spacing: 4
                Label {
                    anchors.horizontalCenter: parent.horizontalCenter
                    text: MaterialIcons.photo_library
                    font.pointSize: 24
                    font.family: MaterialIcons.fontFamily
                }
                Label {
                    text: "Drop Image Files / Folders"
                }
            }

            DropArea {
                id: dropArea
                anchors.fill: parent
                enabled: !root.readOnly
                keys: ["text/uri-list"]
                // TODO: onEntered: call specific method to filter files based on extension
                onDropped: {
                    var augmentSfm = augmentArea.hovered
                    root.filesDropped(drop, augmentSfm)
                }

                // Background opacifier
                Rectangle {
                    visible: dropArea.containsDrag
                    anchors.fill: parent
                    color: root.palette.window
                    opacity: 0.8
                }

                ColumnLayout {
                    anchors.fill: parent
                    visible: dropArea.containsDrag
                    spacing: 1
                    Label {
                        id: addArea
                        property bool hovered: dropArea.drag.y < height
                        Layout.fillWidth: true
                        Layout.fillHeight: true
                        horizontalAlignment: Text.AlignHCenter
                        verticalAlignment: Text.AlignVCenter
                        text: "Add Images"
                        font.bold: true
                        background: Rectangle {
                            color: parent.hovered ? parent.palette.highlight : parent.palette.window
                            opacity: 0.8
                            border.color: parent.palette.highlight
                        }
                    }

                    // DropArea overlay
                    Label {
                        id: augmentArea
                        property bool hovered: visible && dropArea.drag.y >= y
                        Layout.fillWidth: true
                        Layout.preferredHeight: parent.height * 0.3
                        horizontalAlignment: Text.AlignHCenter
                        verticalAlignment: Text.AlignVCenter
                        text: "Augment Reconstruction"
                        font.bold: true
                        wrapMode: Text.WrapAtWordBoundaryOrAnywhere
                        visible: viewpoints.count > 0
                        background: Rectangle {
                            color: parent.hovered ? palette.highlight : palette.window
                            opacity: 0.8
                            border.color: parent.palette.highlight
                        }
                    }
                }
            }
        }

        RowLayout {
            Layout.fillHeight: false
            visible: root.cameraInits.count > 1
            Layout.alignment: Qt.AlignHCenter
            spacing: 2

            ToolButton {
                text: MaterialIcons.navigate_before
                font.family: MaterialIcons.fontFamily
                ToolTip.text: "Previous Group (Alt+Left)"
                ToolTip.visible: hovered
                enabled: nodesCB.currentIndex > 0
                onClicked: nodesCB.decrementCurrentIndex()
            }
            Label { text: "Group " }
            ComboBox {
                id: nodesCB
                model: root.cameraInits.count
                implicitWidth: 40
                currentIndex: root.currentIndex
                onActivated: root.currentIndex = currentIndex

            }
            Label { text: "/ " + (root.cameraInits.count - 1) }
            ToolButton {
                text: MaterialIcons.navigate_next
                font.family: MaterialIcons.fontFamily
                ToolTip.text: "Next Group (Alt+Right)"
                ToolTip.visible: hovered
                enabled: root.currentIndex < root.cameraInits.count - 1
                onClicked: nodesCB.incrementCurrentIndex()
            }
        }
    }

    footerContent: RowLayout {
        anchors.fill: parent

        // Image count
        Label {
            Layout.fillWidth: true
            text: grid.model.count + " image" + (grid.model.count > 1 ? "s" : "") + (_reconstruction.nbCameras > 0 ? " / " + _reconstruction.nbCameras + " camera" + (_reconstruction.nbCameras > 1 ? "s": "") : "")
            elide: Text.ElideRight
        }

        // Thumbnail size icon and slider
        Label {
            text: MaterialIcons.photo_size_select_large
            font.family: MaterialIcons.fontFamily
            font.pixelSize: 13
        }
        Slider {
            id: thumbnailSizeSlider
            from: 70
            value: defaultCellSize
            to: 250
            implicitWidth: 70
            height: parent.height
        }
    }

}
